#include "Keyboard.hpp"
#include <thread>

bool Keyboard::_init_status = false;
long long Keyboard::_pressed_time;
size_t Keyboard::_default_key_hold_time = 600;
size_t Keyboard::_pressed_key_hold_time = 40;
std::vector<int> Keyboard::_Key_States;
std::vector<size_t> Keyboard::_Key_Hold_Times;
std::map<int, int> Keyboard::_Key_Code_Bindings;
std::map<int, std::pair<char, char>> Keyboard::_Key_Capital_Bindings;

long long _GetCurrentTimeInMilliseconds()
{
	static LARGE_INTEGER s_frequency;
	static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
	if (s_use_qpc)
	{
		LARGE_INTEGER _time_now;
		QueryPerformanceCounter(&_time_now);
		return (1000LL * _time_now.QuadPart) / s_frequency.QuadPart;
	}
	else
		return GetTickCount();
}

int Keyboard::GetKeyCode(char key)
{
	return VkKeyScan(key);
}

void Keyboard::PollEvents()
{
	for (unsigned int _key = 1; _key <= 74; _key++)
	{
		if (GetAsyncKeyState(GetVirtualKeyCode(_key)) & 0x8000)
		{
			long long _current_time = _GetCurrentTimeInMilliseconds();
			if (GetKeyState(_key) == MC_NOKEY)
			{
				_Key_States[_key] |= MC_KEY_PRESSED;
				_pressed_time = _current_time;
			}
			else if (GetKeyState(_key) & MC_KEY_PRESSED)
			{
				_Key_States[_key] &= ~(MC_KEY_PRESSED);
				_Key_States[_key] |= MC_KEY_DOWN;
			}
			else if (_Key_States[_key] & MC_KEY_DOWN)
			{
				// updating pressed state when key is held down
				if (_current_time - _pressed_time >= (long long)_Key_Hold_Times[_key])
				{
					_Key_States[_key] |= MC_KEY_PRESSED;
					_pressed_time = _current_time;
					_Key_Hold_Times[_key] = _pressed_key_hold_time;
				}
				else
					_Key_States[_key] &= ~(MC_KEY_PRESSED);
			}
		}
		else
		{
			if (GetKeyState(_key) & MC_KEY_DOWN || GetKeyState(_key) & MC_KEY_PRESSED)
			{
				_Key_States[_key] &= ~(MC_KEY_DOWN);
				_Key_States[_key] &= ~(MC_KEY_PRESSED);
				_Key_States[_key] |= MC_KEY_RELEASED;
			}
			else if (GetKeyState(_key) & MC_KEY_RELEASED)
				_Key_States[_key] = MC_NOKEY;

			// resetting key hold time
			_Key_Hold_Times[_key] = _default_key_hold_time;
		}
	}
}

void Keyboard::Initialize()
{
	_Key_States.clear();
	for (size_t i = 0; i < 76; i++)
	{
		_Key_States.push_back(MC_NOKEY);
		_Key_Hold_Times.push_back(_default_key_hold_time);                                                                         
	}


	// Key Code Bindings
	_Key_Code_Bindings[MC_KEY0] = 0x30;
	_Key_Code_Bindings[MC_KEY1] = 0x31;
	_Key_Code_Bindings[MC_KEY2] = 0x32;
	_Key_Code_Bindings[MC_KEY3] = 0x33;
	_Key_Code_Bindings[MC_KEY4] = 0x34;
	_Key_Code_Bindings[MC_KEY5] = 0x35;
	_Key_Code_Bindings[MC_KEY6] = 0x36;
	_Key_Code_Bindings[MC_KEY7] = 0x37;;
	_Key_Code_Bindings[MC_KEY8] = 0x38;
	_Key_Code_Bindings[MC_KEY9] = 0x39;

	_Key_Code_Bindings[MC_KEY_A] = 0x41;
	_Key_Code_Bindings[MC_KEY_B] = 0x42;
	_Key_Code_Bindings[MC_KEY_C] = 0x43;
	_Key_Code_Bindings[MC_KEY_D] = 0x44;
	_Key_Code_Bindings[MC_KEY_E] = 0x45;
	_Key_Code_Bindings[MC_KEY_F] = 0x46;
	_Key_Code_Bindings[MC_KEY_G] = 0x47;
	_Key_Code_Bindings[MC_KEY_H] = 0x48;
	_Key_Code_Bindings[MC_KEY_I] = 0x49;
	_Key_Code_Bindings[MC_KEY_J] = 0x4A;
	_Key_Code_Bindings[MC_KEY_K] = 0x4B;
	_Key_Code_Bindings[MC_KEY_L] = 0x4C;
	_Key_Code_Bindings[MC_KEY_M] = 0x4D;
	_Key_Code_Bindings[MC_KEY_N] = 0x4E;
	_Key_Code_Bindings[MC_KEY_O] = 0x4F;
	_Key_Code_Bindings[MC_KEY_P] = 0x50;
	_Key_Code_Bindings[MC_KEY_Q] = 0x51;
	_Key_Code_Bindings[MC_KEY_R] = 0x52;
	_Key_Code_Bindings[MC_KEY_S] = 0x53;
	_Key_Code_Bindings[MC_KEY_T] = 0x54;
	_Key_Code_Bindings[MC_KEY_U] = 0x55;
	_Key_Code_Bindings[MC_KEY_V] = 0x56;
	_Key_Code_Bindings[MC_KEY_W] = 0x57;
	_Key_Code_Bindings[MC_KEY_X] = 0x58;
	_Key_Code_Bindings[MC_KEY_Y] = 0x59;
	_Key_Code_Bindings[MC_KEY_Z] = 0x5A;

	_Key_Code_Bindings[MC_F1] = VK_F1;
	_Key_Code_Bindings[MC_F2] = VK_F2;
	_Key_Code_Bindings[MC_F3] = VK_F3;
	_Key_Code_Bindings[MC_F4] = VK_F4;
	_Key_Code_Bindings[MC_F5] = VK_F5;
	_Key_Code_Bindings[MC_F6] = VK_F6;
	_Key_Code_Bindings[MC_F7] = VK_F7;
	_Key_Code_Bindings[MC_F8] = VK_F8;
	_Key_Code_Bindings[MC_F9] = VK_F9;
	_Key_Code_Bindings[MC_F10] = VK_F10;
	_Key_Code_Bindings[MC_F11] = VK_F11;
	_Key_Code_Bindings[MC_F12] = VK_F12;

	_Key_Code_Bindings[MC_BACKSPACE] = VK_BACK;
    _Key_Code_Bindings[MC_SPACE] = VK_SPACE;
    _Key_Code_Bindings[MC_BACKTICK] = VkKeyScan('`');
    _Key_Code_Bindings[MC_ENTER] = VK_RETURN;
    _Key_Code_Bindings[MC_CAPSLOCK] = VK_CAPITAL;
    _Key_Code_Bindings[MC_TAB] = VK_TAB;
    _Key_Code_Bindings[MC_LSHIFT] = VK_LSHIFT;
    _Key_Code_Bindings[MC_RSHIFT] = VK_RSHIFT;
    _Key_Code_Bindings[MC_LCONTROL] = VK_LCONTROL;
    _Key_Code_Bindings[MC_RCONTROL] = VK_RCONTROL;
    _Key_Code_Bindings[MC_ALT] = VK_MENU;
    _Key_Code_Bindings[MC_OPTION] = VK_MENU;
    _Key_Code_Bindings[MC_UP] = VK_UP;
    _Key_Code_Bindings[MC_DOWN] = VK_DOWN;
    _Key_Code_Bindings[MC_RIGHT] = VK_RIGHT;
    _Key_Code_Bindings[MC_LEFT] = VK_LEFT;

    _Key_Code_Bindings[MC_MINUS] = VkKeyScan('-');
    _Key_Code_Bindings[MC_EQUALS] = VkKeyScan('=');
    _Key_Code_Bindings[MC_COMMA] = VkKeyScan(',');
    _Key_Code_Bindings[MC_PERIOD] = VkKeyScan('.');
    _Key_Code_Bindings[MC_SEMICOLON] = VkKeyScan(';');
    _Key_Code_Bindings[MC_TICK] = VK_OEM_7;
    _Key_Code_Bindings[MC_SQUARE_BRACKET_RIGHT] = VkKeyScan('[');
    _Key_Code_Bindings[MC_SQUARE_BRACKET_LEFT] = VkKeyScan(']');
    _Key_Code_Bindings[MC_BACKSLASH] = VkKeyScan('\\');
    _Key_Code_Bindings[MC_SLASH] = VkKeyScan('/');

	// Capital Key Bindings
	_Key_Capital_Bindings[MC_KEY0] = std::pair<char, char>('0', ')');
	_Key_Capital_Bindings[MC_KEY1] = std::pair<char, char>('1', '!');
	_Key_Capital_Bindings[MC_KEY2] = std::pair<char, char>('2', '@');
	_Key_Capital_Bindings[MC_KEY3] = std::pair<char, char>('3', '#');
	_Key_Capital_Bindings[MC_KEY4] = std::pair<char, char>('4', '$');
	_Key_Capital_Bindings[MC_KEY5] = std::pair<char, char>('5', '%');
	_Key_Capital_Bindings[MC_KEY6] = std::pair<char, char>('6', '^');
	_Key_Capital_Bindings[MC_KEY7] = std::pair<char, char>('7', '&');
	_Key_Capital_Bindings[MC_KEY8] = std::pair<char, char>('8', '*');
	_Key_Capital_Bindings[MC_KEY9] = std::pair<char, char>('9', '(');

	_Key_Capital_Bindings[MC_KEY_A] = std::pair<char, char>('a', 'A');
	_Key_Capital_Bindings[MC_KEY_B] = std::pair<char, char>('b', 'B');
	_Key_Capital_Bindings[MC_KEY_C] = std::pair<char, char>('c', 'C');
	_Key_Capital_Bindings[MC_KEY_D] = std::pair<char, char>('d', 'D');
	_Key_Capital_Bindings[MC_KEY_E] = std::pair<char, char>('e', 'E');
	_Key_Capital_Bindings[MC_KEY_F] = std::pair<char, char>('f', 'F');
	_Key_Capital_Bindings[MC_KEY_G] = std::pair<char, char>('g', 'G');
	_Key_Capital_Bindings[MC_KEY_H] = std::pair<char, char>('h', 'H');
	_Key_Capital_Bindings[MC_KEY_I] = std::pair<char, char>('i', 'I');
	_Key_Capital_Bindings[MC_KEY_J] = std::pair<char, char>('j', 'J');
	_Key_Capital_Bindings[MC_KEY_K] = std::pair<char, char>('k', 'K');
	_Key_Capital_Bindings[MC_KEY_L] = std::pair<char, char>('l', 'L');
	_Key_Capital_Bindings[MC_KEY_M] = std::pair<char, char>('m', 'M');
	_Key_Capital_Bindings[MC_KEY_N] = std::pair<char, char>('n', 'N');
	_Key_Capital_Bindings[MC_KEY_O] = std::pair<char, char>('o', 'O');
	_Key_Capital_Bindings[MC_KEY_P] = std::pair<char, char>('p', 'P');
	_Key_Capital_Bindings[MC_KEY_Q] = std::pair<char, char>('q', 'Q');
	_Key_Capital_Bindings[MC_KEY_R] = std::pair<char, char>('r', 'R');
	_Key_Capital_Bindings[MC_KEY_S] = std::pair<char, char>('s', 'S');
	_Key_Capital_Bindings[MC_KEY_T] = std::pair<char, char>('t', 'T');
	_Key_Capital_Bindings[MC_KEY_U] = std::pair<char, char>('u', 'U');
	_Key_Capital_Bindings[MC_KEY_V] = std::pair<char, char>('v', 'V');
	_Key_Capital_Bindings[MC_KEY_W] = std::pair<char, char>('w', 'W');
	_Key_Capital_Bindings[MC_KEY_X] = std::pair<char, char>('x', 'X');
	_Key_Capital_Bindings[MC_KEY_Y] = std::pair<char, char>('y', 'Y');
	_Key_Capital_Bindings[MC_KEY_Z] = std::pair<char, char>('z', 'Z');

	_Key_Capital_Bindings[MC_F1] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F2] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F3] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F4] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F5] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F6] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F7] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F8] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F9] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F10] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F11] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_F12] = std::pair<char, char>('\0', '\0');

	_Key_Capital_Bindings[MC_BACKSPACE] = std::pair<char, char>('\b', '\b');
	_Key_Capital_Bindings[MC_SPACE] = std::pair<char, char>(' ', ' ');
	_Key_Capital_Bindings[MC_BACKTICK] = std::pair<char, char>('`', '~');
	_Key_Capital_Bindings[MC_ENTER] = std::pair<char, char>('\n', '\n');
	_Key_Capital_Bindings[MC_CAPSLOCK] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_TAB] = std::pair<char, char>('\t', '\t');
	_Key_Capital_Bindings[MC_LSHIFT] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_RSHIFT] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_LCONTROL] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_RCONTROL] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_ALT] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_OPTION] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_UP] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_DOWN] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_RIGHT] = std::pair<char, char>('\0', '\0');
	_Key_Capital_Bindings[MC_LEFT] = std::pair<char, char>('\0', '\0');

	_Key_Capital_Bindings[MC_MINUS] = std::pair<char, char>('-', '_');
	_Key_Capital_Bindings[MC_EQUALS] = std::pair<char, char>('=', '+');
	_Key_Capital_Bindings[MC_COMMA] = std::pair<char, char>(',', '<');
	_Key_Capital_Bindings[MC_PERIOD] = std::pair<char, char>('.', '>');
	_Key_Capital_Bindings[MC_SEMICOLON] = std::pair<char, char>(';', ':');
	_Key_Capital_Bindings[MC_TICK] = std::pair<char, char>('\'', '\"');
	_Key_Capital_Bindings[MC_SQUARE_BRACKET_RIGHT] = std::pair<char, char>('[', '{');
	_Key_Capital_Bindings[MC_SQUARE_BRACKET_LEFT] = std::pair<char, char>(']', '}');
	_Key_Capital_Bindings[MC_BACKSLASH] = std::pair<char, char>('\\', '|');
	_Key_Capital_Bindings[MC_SLASH] = std::pair<char, char>('/', '?');
	
	_init_status = true;
}

void Keyboard::Shutdown()
{
	_init_status = false;
}

bool Keyboard::IsKeyDown(int key)
{
	return (_Key_States[key] == MC_KEY_DOWN);
}

bool Keyboard::IsKeyPressed(int key)
{
	return (_Key_States[key] == MC_KEY_PRESSED);
}

bool Keyboard::IsKeyReleased(int key)
{
	return (_Key_States[key] == MC_KEY_RELEASED);
}

void Keyboard::SimulateKeypress(int key)
{
	INPUT input;

	// Set up a generic keyboard event.
	input.type = INPUT_KEYBOARD;
	input.ki.wScan = 0; // hardware scan code for key
	input.ki.time = 0;
	input.ki.dwExtraInfo = 0;

	input.ki.wVk = key;
	input.ki.dwFlags = 0;

	// Send key PRESS
	SendInput(1, &input, sizeof(INPUT));

	input.ki.dwFlags = KEYEVENTF_KEYUP;

	// Send key RELEASE
	SendInput(1, &input, sizeof(INPUT));
}

void Keyboard::SimulateKeypressSequence(std::string msg)
{
	for (char c : msg)
	{
		SimulateKeypress(Keyboard::GetKeyCode(c));
	}
}

void Keyboard::SimulateKeypressSequence(std::wstring msg)
{
	for (wchar_t c : msg)
	{
		SimulateKeypress(Keyboard::GetKeyCode((char)c));
	}
}

void Keyboard::SimulateKeypressSequence(char* msg)
{
	for (int i = 0; i < (int)strlen(msg); i++)
	{
		SimulateKeypress(Keyboard::GetKeyCode(msg[i]));
	}
}

void Keyboard::SimulateKeypressWithShift(int key)
{
	INPUT keyInput;

	// Set up a generic keyboard event.
	keyInput.type = INPUT_KEYBOARD;
	keyInput.ki.wScan = 0; // hardware scan code for key
	keyInput.ki.time = 0;
	keyInput.ki.dwExtraInfo = 0;

	keyInput.ki.wVk = MC_LSHIFT;
	keyInput.ki.dwFlags = 0;

	SendInput(1, &keyInput, sizeof(INPUT));	// shift down
	keyInput.ki.wVk = key;
	SendInput(1, &keyInput, sizeof(INPUT));	// key down

	keyInput.ki.dwFlags = KEYEVENTF_KEYUP;
	SendInput(1, &keyInput, sizeof(INPUT));	// key up
	keyInput.ki.wVk = MC_LSHIFT;
	SendInput(1, &keyInput, sizeof(INPUT));	// shift up
}

void Keyboard::SimulateKeypressSequenceWithShift(std::string msg)
{
	for (char c : msg)
	{
		SimulateKeypressWithShift(Keyboard::GetKeyCode(c));
	}
}

void Keyboard::SimulateKeypressSequenceWithShift(std::wstring msg)
{
	for (wchar_t c : msg)
	{
		SimulateKeypressWithShift(Keyboard::GetKeyCode((char)c)); 
	}
}

void Keyboard::SimulateKeypressSequenceWithShift(char* msg)
{
	for (int i = 0; i < (int)strlen(msg); i++)
	{
		SimulateKeypressWithShift(Keyboard::GetKeyCode(msg[i]));
	}
}

int Keyboard::GetKeyState(int _key)
{
	if (_Key_States.size() > 0)
	{
		if (_key > 0 && _key < (int)_Key_States.size())
			return _Key_States[_key];
		else
			return -1;
	}
	else return -1;
}

char Keyboard::ToChar(int _key)
{
	return (char)(_Key_Code_Bindings[_key]);
}

int Keyboard::GetVirtualKeyCode(int _key)
{
	return _Key_Code_Bindings[_key];
}

char Keyboard::GetKeyValue(int _key, bool _capital, bool _caps_lock_enabled)
{
	if (_key < 0 || _key >(int)_Key_Capital_Bindings.size()) return 0;

	// a-z for when caps lock is also toggled
	if (_key > 10 && _key < 37 && (_capital || _caps_lock_enabled))
		return _Key_Capital_Bindings[_key].second;

	if (_capital)
		return _Key_Capital_Bindings[_key].second;
	else			
		return _Key_Capital_Bindings[_key].first;
}
